package spacetrussOpt;

import data.DataTest;
import geneticalgorithm_masterworker.GAMaster;
import inf.minife.fe.Model;
import model.SpaceTruss;
import mpi.MPI;

/*
 * This program based on method proposed in
 * "Using Genetic Algorithm for Optimal Design of Trusses"
 * C.A. Coello Coello, M. Rudnick, A. D. Christiansen
 */

/* 
 * This program is similar to Plane Truss GA Optimize
 * with a small diffrence in Space Truss
 * area values is defined in cross_section_table_sp.dat
 */
/*
 * The objection function is mass value of model:
 * F(x) = Sum(rho*Aj*Lj)
 * design values is cross section area of 10 truss elements
 * Aj with j = 1..10
 * that is included taken as discreted values from
 * American Institute of Steel Construction Manual
 */
public class SpaceTrussGAOpt {
	
	/*
	 * The set of areas available for this truss
	 * according to Rajeev and Krishnamoorthy
	 * S = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8
	 * 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7,
	 * 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6,
	 * 2.8, 3.0, 3.2, 3.4}
	 */
	
	/* Data of Space Truss 
	 * E = 1x10^4 ksi (6.89x10^4 MPa)
	 * rho = 0.10 lb/in^3 (2,770 kg.m^3)
	 * sigma max = +-40,000 psi
	 * u max = +- 0.35 in
	 */
	
	final double sigmaMax_ = 40; // ksi 
	final double uMax_= 0.35; // inch 
	
	/*
	 * Default values for genetic algorithm
	 * we use 5 bit to represent 30 values of possible 
	 * discreted cross section values for 8 groups of elements
	 */
	int numberofgeneration_ = 200;
	int numberofgene_ = 5;
	int numberofchromosome_ = 8;
	float crossoverfactor_ = 0.8f;
	float mutationfactor_ = 0.01f;
	double[] x;
	
	/*
	 * In this program, we use plane truss model
	 * with 10 truss elements and 6 nodes
	 * follow the model of Rajeev and Krishnamoorthy
	 * "Discrete optimization of structure using genetic algorithm"
	 */
	SpaceTruss spacetruss_;
	Model model;	
	

	public static void main (String[] args){
		new SpaceTrussGAOpt(args);
	}
	
	public SpaceTrussGAOpt(String[] args) {
		
		/* Values for MPI */ 
		int myid;
		int nprocs;
		
		/* Setup model */
		spacetruss_ = new SpaceTruss();
		model = spacetruss_.getModel();
		
		//model.solve();

		/* Add master component */		
		GAMaster gamaster = new GAMaster();
		
		/* number of variables for design 
		 * cross section area of 10 elements
		 */
		
		int design_number = numberofchromosome_; 
		
		/*
		 * Number of result that worker will send to master 
		 */
		int result_number = 4;
		
		/* Initiating MPI by starting master */	
		gamaster.startMPI(args);
		myid = gamaster.getID();
		nprocs = gamaster.getNumberOfProcessors();
		
		/* Set up number of workers */
		gamaster.setNumberOfWorker(nprocs-1);
		MPI.COMM_WORLD.Barrier();
		
		/* Call master or worker code as appropriate */
		
		if (myid == 0){	
			
			
			/* Start apply model plane truss */
			try{
				model.solve();
				
				/* Calculate mass value of default plane truss */
				double massvalue = model.getTotalMass();
				System.out.println("Init total mass = " + massvalue);
				callMaster(gamaster.getNumberOfWorkers(), gamaster, result_number, 
						massvalue, numberofgeneration_, numberofgene_);
			}
			catch (NullPointerException e) {
				System.out.println("Null pointer Exception for space truss! Init it before use ");
				return;
			}
			MPI.COMM_WORLD.Barrier();
		}
		else{	
			
			try{				
				//model.solve();
				callWorker(myid, design_number, sigmaMax_, uMax_, spacetruss_, 
						numberofgene_, crossoverfactor_, mutationfactor_, result_number);
			}
			catch (NullPointerException e) {
				System.out.println("Null pointer Exception for space truss! Init it before use ");
				return;
			}
			MPI.COMM_WORLD.Barrier();
		}
		MPI.Finalize();
	}

	

	private void callMaster(int numberOfWorkers, GAMaster gamaster,
			int result_number, double massvalue, int numberofgeneration, int numberofgene) {
		// TODO Auto-generated method stub
		/* 
		 * Read the values from table of posible cross section areas from cross_section_table.dat files 
		 */
		int nmax = 30;	//default number of values will be read	= 42
		
		double[] a;
		int n = 0;
		
		/* Number of generation for GA */
		int masterID = 0;		
		
		a = gamaster.prepareWork(n, nmax, "../Sourcecode_Week7/src/cross_section_table_sp.dat");
		
		/*
		 * We have an array of discrete values for cross section area
		 * then, we decode that array into 5 bit string representation
		 * 2^5 = 32 values =  30 values + 2 extra duplicated values from array
		 */
		int area_num = (int)Math.pow(2, numberofgene);
		
		double[] list_area = new double[area_num];
		
		
		for (int i = 0; i < list_area.length; i++){
			if(i < a.length){
				list_area[i] = a[i];
			}
			else{
				int temp = (int)(Math.random()*a.length);
				list_area[i] = a[temp];
			}
		}
		
		/* Init new array store list to broad cast */		
		DataTest[] listbcast = new DataTest[list_area.length];
		
		try{
			for (int i = 0; i < list_area.length; i++){
				double temp = list_area[i];
				listbcast[i] = new DataTest(temp);
			}
		}
		catch (NullPointerException e) {
			System.out.println("Array has null pointer! Init before use");
			return;
		}
		
		/*
		 * Master broadcast the list of
		 * cross section areas to all workers
		 */
		MPI.COMM_WORLD.Bcast(listbcast, 0, listbcast.length, MPI.OBJECT, masterID);		
		
		/* Start timing */
		gamaster.startTime();
		
		String filename = "../Sourcecode_Week7/src/spacetruss_ga_result.dat";
		
		/* Start parallel work assignment */
		gamaster.doParallel(masterID, numberOfWorkers, result_number, numberofgeneration, filename);
		
		/* End timing */
		gamaster.endTime();
		
		double[] starttime = gamaster.getStarTime();
		double[] endtime = gamaster.getEndTime();
		
		/* Print out */
		System.out.println("\n Number of workers = " + numberOfWorkers);
		
		//System.out.println("\n Maximum iteration = " + maxiter);
		
		System.out.println("\n Execution MPI time = " + (endtime[0]-starttime[0]));
		System.out.println("\n Execution system time = " + (endtime[1]-starttime[1])/1000);
		
	}
	
	private void callWorker(int myid, int design_number, double sigmaMax,
			double uMax, SpaceTruss spacetruss, int numberofgene,
			float crossoverfactor, float mutationfactor, int result_number) {
		// TODO Auto-generated method stub
		int master = 0;
		
		/* Start worker */
		SPGAWorker spgaworker = new SPGAWorker(myid);
		
		/* Number of posible values for cross section list
		 * = 2^number of chomosome that used to
		 * represent a value in list		
		 */
		int area_num = (int)Math.pow(2, numberofgene);
		
		/* Worker receive the list broad cast from master */
		DataTest[] listbcast = new DataTest[area_num];
		
		
		MPI.COMM_WORLD.Bcast(listbcast, 0, area_num, MPI.OBJECT, master);		

		/* Do work */
		spgaworker.doWork(myid, design_number, master, listbcast, spacetruss, sigmaMax, uMax, 
				numberofgene, crossoverfactor, mutationfactor, result_number);
		
	}
}
